"""
ConnLLM提供者接口和工厂
定义与LLM交互的标准接口和工厂函数
"""
from abc import ABC, abstractmethod
import importlib
import logging
import time
import warnings
import re
from typing import Dict, List, Optional, Any, Generator, Type, Tuple, Set

from .constants import REMOTE_PROVIDERS, LOCAL_PROVIDERS, API_BASE_URLS, DEFAULT_TIMEOUT
from .exceptions import (
	LLMException, ProviderError, AuthenticationError, QuotaExceededError,
	RateLimitError, ConnectionError, TimeoutError, ServerError,
	InvalidRequestError, ConfigurationError, map_provider_error
)
from .provider_registry import get_registry

# 设置日志
logger = logging.getLogger("connllm")


class LLMProvider(ABC):
	"""LLM提供者基类"""
	
	@abstractmethod
	def complete(self, messages: List[Dict[str, str]], stream: bool = False) -> str:
		"""
		获取LLM完成
		
		Args:
			messages: 消息列表，格式为[{"role": "user", "content": "..."}]
			stream: 是否使用流式响应
			
		Returns:
			完成的文本
		"""
		pass
	
	@abstractmethod
	def complete_stream(self, messages: List[Dict[str, str]]) -> Generator[str, None, None]:
		"""
		获取LLM流式完成
		
		Args:
			messages: 消息列表
			
		Returns:
			流式响应生成器
		"""
		pass
		
	def filter_chat_args(self, *args, **kwargs) -> tuple:
		"""
		过滤和处理API调用参数
		
		Args:
			*args: 位置参数
			**kwargs: 关键字参数
			
		Returns:
			处理后的(args, kwargs)元组
		"""
		return args, kwargs
		
	def extract_reasoning(self, data: Dict[str, Any]) -> Optional[str]:
		"""
		从响应数据中提取思考过程
		
		Args:
			data: 响应数据
			
		Returns:
			思考过程文本，如果不存在则返回None
		"""
		return None
		
	def calculate_tokens(self, response: Dict[str, Any]) -> Tuple[int, int]:
		"""
		计算输入和输出令牌数
		
		Args:
			response: 响应数据
			
		Returns:
			(输入令牌数, 输出令牌数)元组
		"""
		# 1. 尝试从可选模块导入函数
		try:
			from ..optional.tokenizers import extract_tokens_from_response
			
			# 提取token信息，包括缓存token
			input_tokens, output_tokens, _, _ = extract_tokens_from_response(
				response, self.provider_type
			)
			
			if input_tokens > 0 or output_tokens > 0:
				return input_tokens, output_tokens
		except ImportError:
			pass  # 继续使用后备方法
			
		# 2. 使用特定提供商的响应格式提取token
		# OpenAI风格
		if isinstance(response, dict) and "usage" in response:
			usage = response["usage"]
			return usage.get("prompt_tokens", 0), usage.get("completion_tokens", 0)
			
		# Anthropic风格
		if isinstance(response, dict) and "message" in response and "usage" in response["message"]:
			usage = response["message"]["usage"]
			return usage.get("input_tokens", 0), usage.get("output_tokens", 0)
		
		# 3. 如果无法从响应中直接获取，提取文本并使用估算方法
		input_text = ""
		output_text = ""
		
		# 尝试从响应中提取文本
		if isinstance(response, dict):
			# 提取输入文本（如果有）
			if "prompt" in response:
				input_text = response["prompt"]
			
			# 提取输出文本（常见模式）
			if "choices" in response and len(response["choices"]) > 0:
				if "message" in response["choices"][0]:
					output_text = response["choices"][0]["message"].get("content", "")
				elif "text" in response["choices"][0]:
					output_text = response["choices"][0]["text"]
					
		# 4. 使用可选的token计算，如果可用
		try:
			from ..optional.tokenizers import count_tokens
			input_tokens = count_tokens(input_text, self.provider_type, self.model) if input_text else 0
			output_tokens = count_tokens(output_text, self.provider_type, self.model) if output_text else 0
		except ImportError:
			# 5. 退回到基本估算
			input_tokens = len(input_text) // 4 if input_text else 0
			output_tokens = len(output_text) // 4 if output_text else 0
		
		return input_tokens, output_tokens
		
	def calculate_cost(self, input_tokens: int, output_tokens: int) -> float:
		"""
		计算API调用成本
		
		Args:
			input_tokens: 输入令牌数
			output_tokens: 输出令牌数
			
		Returns:
			成本（美元）
		"""
		try:
			from ..core.pricing import calculate_cost
			return calculate_cost(
				self.provider_type,
				self.model,
				input_tokens,
				output_tokens
			)
		except (ImportError, AttributeError):
			return 0.0  # 默认实现返回0


class ProviderFactory:
	"""提供商工厂类"""
	
	@classmethod
	def register(cls, name: str, provider_class: Type[LLMProvider]) -> None:
		"""
		注册提供商类（兼容旧API）
		
		Args:
			name: 提供商名称
			provider_class: 提供商类
		"""
		registry = get_registry()
		registry.register(name, provider_class)
	
	@classmethod
	def create(cls, config: Dict[str, Any]) -> LLMProvider:
		"""
		根据配置创建LLM提供商实例
		
		Args:
			config: 配置字典，包含provider, model等参数
			
		Returns:
			LLMProvider实例
		"""
		provider_type = config.get("provider", "anthropic")
		registry = get_registry()
		
		# 尝试获取提供商类
		provider_class = registry.get(provider_type)
		
		if not provider_class:
			# 如果提供商类不存在，尝试导入
			success = registry.import_provider(provider_type)
			
			if success:
				provider_class = registry.get(provider_type)
			else:
				# 尝试从内置提供商目录导入
				try:
					importlib.import_module(f"connllm.providers.{provider_type}")
					provider_class = registry.get(provider_type)
				except ImportError:
					pass
					
				if not provider_class:
					# 尝试加载所有已发现的提供商
					registry.discover_providers()
					provider_class = registry.get(provider_type)
					
					if not provider_class:
						raise ValueError(f"不支持的LLM提供者类型: {provider_type}")
		
		# 创建提供商实例
		return provider_class(config)
	
	@classmethod
	def discover_providers(cls) -> List[str]:
		"""
		发现所有可用的提供商
		
		Returns:
			提供商名称列表
		"""
		registry = get_registry()
		registry.discover_providers()
		return registry.list_providers()
		
	@classmethod
	def is_remote_provider(cls, provider_type: str) -> bool:
		"""
		判断是否为远程提供商
		
		Args:
			provider_type: 提供商类型
			
		Returns:
			是否为远程提供商
		"""
		return provider_type in REMOTE_PROVIDERS
		
	@classmethod
	def is_local_provider(cls, provider_type: str) -> bool:
		"""
		判断是否为本地提供商
		
		Args:
			provider_type: 提供商类型
			
		Returns:
			是否为本地提供商
		"""
		return provider_type in LOCAL_PROVIDERS
		
	@classmethod
	def get_provider_base_url(cls, provider_type: str) -> str:
		"""
		获取提供商的基础URL
		
		Args:
			provider_type: 提供商类型
			
		Returns:
			基础URL
		"""
		return API_BASE_URLS.get(provider_type, "")


class BaseProvider(LLMProvider):
	"""基础提供商实现，包含通用功能"""
	
	def __init__(self, config: Dict[str, Any]):
		"""
		初始化基础提供商
		
		Args:
			config: 配置字典
		"""
		self.config = config
		self.provider_type = config.get("provider", "anthropic")
		self.model = config.get("model", "claude-3-5-sonnet")
		self.max_retries = config.get("max_retries", 3)
		self.retry_delay = config.get("retry_delay", 1.0)
		self.temperature = config.get("temperature", 0.1)
		self.max_tokens = config.get("max_tokens", 4096)
		self.timeout = config.get("timeout", DEFAULT_TIMEOUT)


class CapabilityProvider(BaseProvider):
	"""
	能力提供商基类
	提供能力管理功能
	"""
	
	def __init__(self, config: Dict[str, Any]):
		"""
		初始化能力提供商
		
		Args:
			config: 配置字典
		"""
		super().__init__(config)
		self._capabilities: Dict[str, Any] = {}
		self._init_capabilities()
	
	def _init_capabilities(self) -> None:
		"""
		初始化功能组件
		由子类实现以注册特定能力
		"""
		pass
	
	def register_capability(self, capability_id: str, implementation_class: Type) -> None:
		"""
		注册功能组件
		
		Args:
			capability_id: 能力标识符
			implementation_class: 能力实现类
		"""
		try:
			capability = implementation_class(self)
			if capability.is_supported():
				self._capabilities[capability_id] = capability
				logger.debug(f"已注册能力: {capability_id}")
				
				# 检查并注册依赖的能力
				self._register_dependencies(capability_id, implementation_class)
		except Exception as e:
			logger.warning(f"注册能力 {capability_id} 失败: {str(e)}")
	
	def _register_dependencies(self, capability_id: str, implementation_class: Type) -> None:
		"""
		注册能力依赖
		
		Args:
			capability_id: 能力ID
			implementation_class: 能力实现类
		"""
		from ..capabilities import get_registered_capability
		
		dependencies = implementation_class.get_dependencies()
		for dep_id in dependencies:
			if dep_id not in self._capabilities:
				dep_class = get_registered_capability(dep_id)
				if dep_class:
					self.register_capability(dep_id, dep_class)
				else:
					logger.warning(f"能力 {capability_id} 依赖未注册的能力: {dep_id}")
	
	def has_capability(self, capability_id: str) -> bool:
		"""
		检查是否支持指定能力
		
		Args:
			capability_id: 能力标识符
			
		Returns:
			是否支持
		"""
		return capability_id in self._capabilities
	
	def get_capability(self, capability_id: str) -> Any:
		"""
		获取能力实例
		
		Args:
			capability_id: 能力标识符
			
		Returns:
			能力实例
			
		Raises:
			UnsupportedOperationError: 如果不支持该能力
		"""
		if not self.has_capability(capability_id):
			from .exceptions import UnsupportedOperationError
			raise UnsupportedOperationError(f"不支持能力: {capability_id}", 
											self.provider_type, self.model)
		return self._capabilities[capability_id]
	
	def get_available_capabilities(self) -> Set[str]:
		"""
		获取所有可用能力列表
		
		Returns:
			能力ID集合
		"""
		return set(self._capabilities.keys())
		
	def _call_with_retry(self, func, *args, **kwargs):
		"""
		带重试机制的API调用
		
		Args:
			func: 要调用的函数
			*args: 位置参数
			**kwargs: 关键字参数
			
		Returns:
			函数返回值
			
		Raises:
			LLMException: 如果所有重试都失败
		"""
		# 1. 尝试使用能力系统的错误处理功能
		if self.has_capability("error_handling"):
			error_handling = self.get_capability("error_handling")
			return error_handling.retry_operation(func, *args, **kwargs)
			
		# 2. 否则使用工具函数
		try:
			from ..utils.retry.backoff import retry_with_backoff
			
			provider_type = getattr(self, 'provider_type', 'unknown')
			model = getattr(self, 'model', None)
			
			return retry_with_backoff(func, provider_type, model, *args, **kwargs)
			
		except ImportError:
			# 3. 如果新的重试机制不可用，使用原始旧机制
			from ..utils.retry import create_retry_strategy
			
			provider_type = getattr(self, 'provider_type', 'unknown')
			model = getattr(self, 'model', None)
			
			# 创建重试策略
			retry_strategy = create_retry_strategy(provider_type)
			
			last_error = None
			for attempt in range(retry_strategy.max_retries + 1):
				try:
					return func(*args, **kwargs)
				except Exception as e:
					# 将原始错误转换为标准LLM异常
					if not isinstance(e, LLMException):
						error = map_provider_error(provider_type, e, model)
					else:
						error = e
					
					last_error = error
					
					# 如果错误不应该重试或已达到最大重试次数，则中断
					if not retry_strategy.should_retry(attempt, error):
						break
					
					# 获取重试延迟时间并等待
					retry_delay = retry_strategy.get_retry_delay(attempt, error)
					logger.warning(
						f"API调用失败({provider_type}/{model}), "
						f"重试 {attempt+1}/{retry_strategy.max_retries}: {str(error)}, "
						f"等待 {retry_delay:.2f}秒"
					)
					time.sleep(retry_delay)
			
			# 所有重试都失败了
			if last_error:
				logger.error(f"所有重试都失败了({provider_type}/{model}): {str(last_error)}")
				raise last_error
				
			raise RuntimeError("重试逻辑错误：既没有返回结果也没有抛出异常")
	
	def extract_thinking(self, response: Dict[str, Any]) -> Optional[str]:
		"""
		从响应中提取思考过程（如Claude Thinking）
		
		Args:
			response: 响应数据
			
		Returns:
			思考过程文本，如果不存在则返回None
		"""
		if not isinstance(response, dict):
			return None
			
		# Claude 3.7 Thinking
		if (self.provider_type == "anthropic" or 
		   (self.provider_type == "openrouter" and "claude" in self.model)) and ":thinking" in self.model:
			if "content" in response and isinstance(response["content"], list):
				for content_item in response["content"]:
					if content_item.get("type") == "thinking":
						return content_item.get("text", "")
		
		return None


class RemoteProvider(CapabilityProvider):
	"""
	远程提供商抽象类，用于访问需要网络连接的LLM服务
	
	特性:
	- 需要API密钥
	- 依赖网络连接
	- 可能需要代理设置
	- 有API使用计费
	
	子类责任:
	1. 实现 _complete_internal 方法处理具体API调用
	2. 实现 complete_stream 方法处理流式响应
	3. 重写 _init_capabilities 方法注册支持的能力
	4. 可选重写 extract_reasoning 方法提取思考过程
	5. 可选重写 calculate_tokens 方法计算token使用量
	"""
	
	def __init__(self, config: Dict[str, Any]):
		"""
		初始化远程提供商
		
		Args:
			config: 配置字典
		"""
		# 初始化特有属性
		self.api_key = config.get("api_key")
		self.api_base = config.get("api_base", API_BASE_URLS.get(config.get("provider", "unknown")))
		self.proxy = config.get("proxy")
		
		# 验证API密钥
		if not self.api_key:
			raise ConfigurationError(f"未提供{config.get('provider', 'unknown')}的API密钥")
		
		# 调用父类初始化 
		super().__init__(config)
		
		# 设置HTTP客户端
		self._http_client = self._setup_http_client()
			
	def _setup_http_client(self):
		"""
		设置HTTP客户端，处理代理等设置
		
		Returns:
			HTTP客户端或None
		"""
		if self.proxy:
			try:
				from openai import DefaultHttpxClient
				logger.info(f"使用代理: {self.proxy}")
				return DefaultHttpxClient(proxy=self.proxy)
			except ImportError:
				logger.warning("未安装openai库，无法使用代理")
		return None
		
	def _complete_internal(self, messages: List[Dict[str, str]]) -> Dict[str, Any]:
		"""
		内部完成方法，由子类实现
		
		Args:
			messages: 消息列表
			
		Returns:
			API响应数据
		"""
		raise NotImplementedError("子类必须实现_complete_internal方法")
		
	def complete(self, messages: List[Dict[str, str]], stream: bool = False) -> str:
		"""
		获取LLM完成
		
		Args:
			messages: 消息列表
			stream: 是否使用流式响应
			
		Returns:
			完成的文本
		"""
		if stream:
			return "".join(self.complete_stream(messages))
			
		response = self._call_with_retry(self._complete_internal, messages)
		
		# 提取响应文本
		if not isinstance(response, dict):
			raise ProviderError(f"无效的API响应: {type(response)}", self.provider_type, self.model)
			
		# 计算Token使用量和成本
		try:
			input_tokens, output_tokens = self.calculate_tokens(response)
			cost = self.calculate_cost(input_tokens, output_tokens)
			
			if input_tokens > 0 or output_tokens > 0:
				logger.debug(
					f"Token使用量({self.provider_type}/{self.model}): "
					f"输入={input_tokens}, 输出={output_tokens}, "
					f"成本=${cost:.6f}"
				)
		except Exception as e:
			logger.debug(f"计算Token使用量和成本失败: {str(e)}")
		
		# 提取思考过程（如果有）
		thinking = self.extract_thinking(response)
		if thinking:
			logger.debug(f"思考过程: {thinking}")
		
		# 从响应中提取文本
		text = self._extract_text_from_response(response)
		if not text:
			raise ProviderError(f"无法从响应中提取文本: {response}", self.provider_type, self.model)
			
		return text
		
	def _extract_text_from_response(self, response: Dict[str, Any]) -> Optional[str]:
		"""
		从API响应中提取文本
		
		Args:
			response: API响应数据
			
		Returns:
			响应文本
		"""
		# Anthropic格式
		if "content" in response:
			if isinstance(response["content"], str):
				return response["content"]
			elif isinstance(response["content"], list):
				text_parts = []
				for content_item in response["content"]:
					if content_item.get("type") == "text":
						text_parts.append(content_item.get("text", ""))
				return "".join(text_parts)
				
		# OpenAI格式
		if "choices" in response and len(response["choices"]) > 0:
			choice = response["choices"][0]
			if "message" in choice and "content" in choice["message"]:
				return choice["message"]["content"]
			elif "text" in choice:
				return choice["text"]
				
		# Moonshot格式
		if "response" in response:
			return response["response"]
		
		# 未知格式
		return None


class LocalProvider(CapabilityProvider):
	"""
	本地提供商抽象类，用于访问本地运行的LLM服务
	
	特性:
	- 通常不需要API密钥
	- 运行在本地环境
	- 无需互联网连接
	- 通常免费使用
	- 可能需要管理本地资源（GPU内存等）
	
	子类责任:
	1. 实现 _complete_internal 方法处理本地模型调用
	2. 实现 complete_stream 方法处理流式响应
	3. 重写 _init_capabilities 方法注册支持的能力
	4. 可选实现模型加载/卸载逻辑
	5. 可选实现资源管理（如GPU内存管理）
	"""
	
	def __init__(self, config: Dict[str, Any]):
		"""
		初始化本地提供商
		
		Args:
			config: 配置字典
		"""
		# 初始化特有属性
		self.host = config.get("host", "localhost")
		self.port = config.get("port", 8000)
		
		# 调用父类初始化
		super().__init__(config)
		
	def calculate_cost(self, input_tokens: int, output_tokens: int) -> float:
		"""
		计算API调用成本 - 本地模型通常免费
		
		Args:
			input_tokens: 输入令牌数
			output_tokens: 输出令牌数
			
		Returns:
			成本（美元）
		"""
		return 0.0  # 本地模型通常免费
